`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2021/12/27 13:41:15
// Design Name: 
// Module Name: arp_tx
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module arp_tx(
input                clk        , //
input                rst        , //

input                arp_tx_en  , //ARP发送使能
input                arp_tx_type, //ARP类型，0为请求，1为应答
input        [47:0]  des_mac    , //目的MAC
input        [31:0]  des_ip     , //目的IP
input        [31:0]  crc_data   , //CRC校验码
output  reg          tx_done    , //发送完成标志
output  reg          gmii_tx_en , //GMII接口
output  reg  [7:0]   gmii_txd     //GMII接口
    );

typedef enum bit [7:0] {
    st_idle,                       //空闲状态
    st_pre,                        //发送序言状态，包括7个字节的前导码和1个字节的SFD,即7个8'h55和1个8'hd5
    st_eth_head,                   //以太网帧头，共14字节，分别为6个字节的目的MAC地址，6个字节的源MAC地址和2个字节的以太网的类型(ARP为16'h0806)
    st_arp_data,                   //46字节ARP数据包，前28字节为数据，后18字节为零填充
    st_crc                         //4字节crc校验码   
} State;

State cur_state,next_state;
logic skip_en;                     //状态跳转信号
logic [31:0] cnt;                  //计数器

parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
parameter BOARD_MAC = 48'h00_11_22_33_44_55;;
parameter DEST_IP = {8'd192,8'd168,8'd1,8'd102};
parameter DEST_MAC= 48'hff_ff_ff_ff_ff_ff;
localparam  ETH_TYPE     = 16'h0806 ; //以太网帧头中的以太网类型
localparam  HD_TYPE      = 16'h0001 ; //ARP数据包中的硬件类型
localparam  PROTOCOL_TYPE= 16'h0800 ; //ARP数据包中的协议类型

logic gmii_tx_en_r;
logic [7:0] eth_head [0:14-1];
logic [7:0] arp_data [0:28-1];
logic [7:0] crc_data_r [0:4-1];
//
assign tx_done=(gmii_tx_en_r==1)&(gmii_tx_en==0);
//gmii_tx_en_r
always_ff@(posedge clk)
    gmii_tx_en_r<=gmii_tx_en;
//crc_data_r
always_ff@(posedge clk,posedge rst)
if(rst)
begin
    for(int i=0;i<4;i++)
        crc_data_r[i]<=0;
end
else if(arp_tx_en)
begin
    crc_data_r[0]<=crc_data[31:24];
    crc_data_r[1]<=crc_data[23:16];
    crc_data_r[2]<=crc_data[15:8];
    crc_data_r[3]<=crc_data[7:0];
end
//eth_head
always_ff@(posedge clk,posedge rst)
if(rst)
    for(int i=0;i<14;i++)
        eth_head[i]<=0;
else if(arp_tx_en)                                    
begin
    //0-5字节为目的MAC地址
    eth_head[0]<=des_mac[47:40];                    //先发送高位字节，后发送低位字节,也可修改为des_mac
    eth_head[1]<=des_mac[39:32];
    eth_head[2]<=des_mac[31:24];
    eth_head[3]<=des_mac[23:16];
    eth_head[4]<=des_mac[15:8];
    eth_head[5]<=des_mac[7:0];
    //6-11字节为源MAC地址
    eth_head[6]<=BOARD_MAC[47:40];
    eth_head[7]<=BOARD_MAC[39:32];
    eth_head[8]<=BOARD_MAC[31:24];
    eth_head[9]<=BOARD_MAC[23:16];
    eth_head[10]<=BOARD_MAC[15:8];
    eth_head[11]<=BOARD_MAC[7:0];
    //12-13字节以太网类型
    eth_head[12]<=ETH_TYPE[15:8];
    eth_head[13]<=ETH_TYPE[7:0];
end
//arp_data
always_ff@(posedge clk,posedge rst)
if(rst)
begin
    for(int i=0;i<28;i++)
        arp_data[i]<=0;
end
else if(arp_tx_en)
begin
    //硬件类型
    arp_data[0]<=HD_TYPE[15:8];
    arp_data[1]<=HD_TYPE[7:0];
    //协议类型
    arp_data[2]<=PROTOCOL_TYPE[15:8];
    arp_data[3]<=PROTOCOL_TYPE[7:0];
    //硬件地址长度
    arp_data[4]<=8'd6;
    //协议地址长度
    arp_data[5]<=8'd4;
    //操作码OP
    arp_data[6]<=8'd0;
    arp_data[7]<=(arp_tx_type==0)?8'd1:8'd2;                   //ARP请求时为1，应答时为2
    //源MAC地址，8-13共6个字节
    arp_data[8]<=BOARD_MAC[47:40];
    arp_data[9]<=BOARD_MAC[39:32];
    arp_data[10]<=BOARD_MAC[31:24];
    arp_data[11]<=BOARD_MAC[23:16];
    arp_data[12]<=BOARD_MAC[15:8];
    arp_data[13]<=BOARD_MAC[7:0];
    //源IP地址，14-17共4字节
    arp_data[14]<=BOARD_IP[31:24];
    arp_data[15]<=BOARD_IP[23:16];
    arp_data[16]<=BOARD_IP[15:8];
    arp_data[17]<=BOARD_IP[7:0];
    //目的MAC地址，18-23共6字节
    arp_data[18]<=des_mac[47:40];        //也可修改为des_mac
    arp_data[19]<=des_mac[39:32];
    arp_data[20]<=des_mac[31:24];
    arp_data[21]<=des_mac[23:16];
    arp_data[22]<=des_mac[15:8];
    arp_data[23]<=des_mac[7:0];
    //目的IP地址，24-27共4字节
    arp_data[24]<=des_ip[31:24];        //也可修改为des_ip
    arp_data[25]<=des_ip[23:16];
    arp_data[26]<=des_ip[15:8];
    arp_data[27]<=des_ip[7:0];
end
//cur_state
always_ff@(posedge clk,posedge rst)
if(rst)
    cur_state<=st_idle;
else
    cur_state<=next_state;
//next_state
always_comb
begin
    case(cur_state)
       st_idle:if(skip_en)
                   next_state=st_pre;
               else
                   next_state=st_idle;
       st_pre:if(skip_en)
                   next_state=st_eth_head;
              else
                   next_state=st_pre;
       st_eth_head:
              if(skip_en)
                   next_state=st_arp_data;
              else
                   next_state=st_eth_head;
       st_arp_data:if(skip_en)
                       next_state=st_crc;
                   else
                       next_state=st_arp_data;
       st_crc:if(skip_en)
                   next_state=st_idle;
              else
                   next_state=st_crc;
        default:next_state=st_idle;
    endcase
end
//skip_en
always_comb
case(cur_state)
    st_idle:if(arp_tx_en)
                skip_en=1;
            else
                skip_en=0;
    st_pre:if(cnt==7)
               skip_en=1;
           else
               skip_en=0;
    st_eth_head:if(cnt==13)
                    skip_en=1;
                else
                    skip_en=0;
    st_arp_data:if(cnt==45)
                    skip_en=1;
                else
                    skip_en=0;
    st_crc:if(cnt==3)
               skip_en=1;
            else
               skip_en=0;
    default:skip_en=0;
endcase
//cnt
always_ff@(posedge clk,posedge rst)
if(rst)
    cnt<=0;
else
begin
case(cur_state)
    st_idle:if(arp_tx_en)
                cnt<=0;
    st_pre:if(cnt==7)
               cnt<=0;
           else
               cnt<=cnt+1;
    st_eth_head:if(cnt==13)
                    cnt<=0;
                else
                    cnt<=cnt+1;
    st_arp_data:if(cnt==45)
                    cnt<=0;
                else
                    cnt<=cnt+1;
    st_crc:if(cnt==3)
               cnt<=0;
            else
               cnt<=cnt+1;
    default:cnt<=0;
endcase
end
//gmii_tx_en
always_ff@(posedge clk,posedge rst)
if(rst)
    gmii_tx_en<=0;
else
begin
    case(cur_state)
        st_idle:gmii_tx_en<=0;
        st_pre:gmii_tx_en<=1;
        st_eth_head:gmii_tx_en<=1;
        st_arp_data:gmii_tx_en<=1;
        st_crc:gmii_tx_en<=1;
        default:gmii_tx_en<=1;
    endcase
end
//gmii_txd
always_ff@(posedge clk,posedge rst)
if(rst)
   gmii_txd<=0;
else
begin
    case(cur_state)
        st_idle:gmii_txd<=0;
        st_pre:if(cnt<=6)
                   gmii_txd<=8'h55;
               else
                   gmii_txd<=8'hd5;
        st_eth_head:gmii_txd<=eth_head[cnt];
        st_arp_data:if(cnt<28)
                        gmii_txd<=arp_data[cnt];
                    else
                        gmii_txd<=0;
        st_crc:gmii_txd<=crc_data_r[cnt];
        default:gmii_txd<=0;
    endcase
end
//
endmodule
